package mbp.message;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * Klasa przeznaczona do kodowania i dekodowania wiadomo�ci w formacie xml
 * Podczas operacji kodowania i dekodowania toworzone sa pomocnicze obiekty
 * zawierajace pola informacyjne z prawdziwych obiektow w celu ulatwienia
 * pobierania i wstawiania informacji z formatu xml 
 * @author Wojciech Wilanski
 *
 */
public class MessageXml 
{	
	//Listy obiektow wystepujace w wiadomosci
	public List<resource> resources;
	public List<service> services;
	//public List<String> servers;
	public List<server> servers;
	public List<offer> offers;
	public List<contract> contracts;
	public List<intention> intentions;
	public List<counteroffer> counteroffers;
	public List<validation> validations;
	public List<utility> utilities;
	public List<extra> extras;
	
	private TagBuilder tagBuilder = new TagBuilder();
	
	private Logger logger = Logger.getLogger(this.getClass());
	
	/**
	 * Konstruktor
	 */
	public MessageXml()
	{
		resources = new ArrayList<resource>();
		services = new ArrayList<service>();
		servers = new ArrayList<server>();
		offers = new ArrayList<offer>();
		contracts = new ArrayList<contract>();
		validations = new ArrayList<validation>();
		utilities = new ArrayList<utility>();
		intentions = new ArrayList<intention>();
		counteroffers = new ArrayList<counteroffer>();
		extras = new ArrayList<extra>();
	}
	
	//public MessageXml(String xmlString)
	//{
	//	
	//}
	
	/**
	 * Zwraca wiadomosc w formacie xml jako string. W wiadomosci umiesczane
	 * sa tylko te obiekty ktore zostaly zainicjalizowane. 
	 * @return
	 */
	public String getXmlAsString()
	{
		String output = null;
		
		try 
		{
			//Stworzenie dokumentu
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db = dbf.newDocumentBuilder();
			Document doc = db.newDocument();
			
			//Stworzenie i zapisanie do dokumentu elementu root <mbp/>
			Element rootElement = doc.createElement("mbp");
			doc.appendChild(rootElement);
			
			//Lista zasoby <resources></resources>
			if(resources.size()>0)
			{
				Element resourcesElement = doc.createElement("resources");
				rootElement.appendChild(resourcesElement);
			
			
				//Zapisanie listy zasob�w - tylko istniej�ce elementy
				for(int i=0; i<resources.size(); i++)
				{
					Element resourceElement = tagBuilder.getResourceTag(doc, resources.get(i), "resource");
					resourcesElement.appendChild(resourceElement);
				}
			}
			
			//Lista uslug <services></services>
			if(services.size()>0)
			{
				Element servicesElement = doc.createElement("services");
				rootElement.appendChild(servicesElement);
				
				for(int i=0; i<services.size(); i++)
				{
					Element service = tagBuilder.getServiceTag(doc, services.get(i), "service");
					servicesElement.appendChild(service);
				}
			}
			
			//Lista serwerow <servers></servers>
			if(servers.size()>0)
			{
				Element serversElement = doc.createElement("servers");
				rootElement.appendChild(serversElement);
				
				for(int i=0; i<servers.size(); i++)
				{
					Element server = doc.createElement("server");
					server.setTextContent(servers.get(i).name);
					server.setAttribute("type", servers.get(i).type);
					
					serversElement.appendChild(server);
				}
			}
			
			//Lista ofert <offers></offers>
			if(offers.size()>0)
			{
				Element offersElement = doc.createElement("offers");
				rootElement.appendChild(offersElement);
				
				for(int i=0; i<offers.size(); i++)
				{
					Element offer = tagBuilder.getOfferTag(doc, offers.get(i), "offer");				
					offersElement.appendChild(offer);
				}
			}
			
			//lista kontraktow <contracts></contracts>
			if(contracts.size()>0)
			{
				Element contractsElement = doc.createElement("contracts");
				rootElement.appendChild(contractsElement);
				
				for(int i=0; i<contracts.size(); i++)
				{
					//Element contract = doc.createElement("contract");
					Element contract = tagBuilder.getContractTag(doc, contracts.get(i), "contract");
					contractsElement.appendChild(contract);
				}
			}
			
			if(validations.size()>0)
			{
				Element validationsElement = doc.createElement("validations");
				rootElement.appendChild(validationsElement);
				
				for(int i=0; i<validations.size(); i++)
				{
					Element validation = tagBuilder.getResourceValidationTag(doc, validations.get(i), "validation");
					validationsElement.appendChild(validation);
				}
			}
			
			if(utilities.size()>0)
			{
				//utworzenie i wstawienie tagu <utilities\> do <mbp\>
				Element utilitiesElement = doc.createElement("utilities");
				rootElement.appendChild(utilitiesElement);
				
				for(int i = 0; i<utilities.size(); i++)
				{			
					//wstawienie tagu <utility\> do <utilities\>Element utilityElement
					Element utilityElement = tagBuilder.getResourceUtilityTag(doc, utilities.get(i), "utility");
					utilitiesElement.appendChild(utilityElement);
				}
			}
			
			
			if(intentions.size()>0)
			{
				Element intentionsElement = doc.createElement("intentions");
				rootElement.appendChild(intentionsElement);
				
				for(int i = 0; i<intentions.size(); i++)
				{
					Element intentionElement = tagBuilder.getIntentionTag(doc, intentions.get(i), "intention");
					intentionsElement.appendChild(intentionElement);
				}
			}
			
			if(counteroffers.size()>0)
			{
				Element counteroffersElement = doc.createElement("counteroffers");
				rootElement.appendChild(counteroffersElement);
				
				for(int i = 0; i<counteroffers.size(); i++)
				{
					Element counterofferElement = tagBuilder.getCounterOfferTag(doc, counteroffers.get(i), "counteroffer");
					counteroffersElement.appendChild(counterofferElement);
				}
			}
			
			if(extras.size()>0)
			{
				Element extrasElement = doc.createElement("extras");
				rootElement.appendChild(extrasElement);
				
				for(int i = 0; i<extras.size(); i++)
				{
					Element extraElement = tagBuilder.getExtraTag(doc, extras.get(i), "extra");
					extrasElement.appendChild(extraElement);
				}
			}
			
			

			
			
			TransformerFactory tf = TransformerFactory.newInstance();
		    Transformer transformer = tf.newTransformer();
		    transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
		    StringWriter writer = new StringWriter();
		    transformer.transform(new DOMSource(doc), new StreamResult(writer));
		    output = writer.getBuffer().toString().replaceAll("\n|\r", "");
			
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			logger.error("ParserConfigurationException", e);
		} catch (TransformerConfigurationException e) {
			// TODO Auto-generated catch block
			logger.error("TransformerConfigurationException", e);
		} catch (TransformerException e) {
			// TODO Auto-generated catch block
			logger.error("TransformerException", e);
		}
		
		
		
		return output;
	}
	
	/**
	 * Dekoduje wiadomosc. Inicjalizowane sa tylko te obiekty ktore wystepuja
	 * w dekodowanej wiadomosci.
	 * @param xmlMessage
	 */
	public void decodeXmlString(String xmlMessage)
	{
		this.contracts.clear();
		this.offers.clear();
		this.resources.clear();
		this.servers.clear();
		this.services.clear();
		this.validations.clear();
		this.utilities.clear();
		this.intentions.clear();
		this.counteroffers.clear();
		this.extras.clear();
		
		DocumentBuilder db;
		try 
		{
			db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			InputSource is = new InputSource();
			is.setCharacterStream(new StringReader(xmlMessage));
			Document doc = db.parse(is);
			
			NodeList resources = doc.getElementsByTagName("resources");
			NodeList services = doc.getElementsByTagName("services");
			NodeList servers = doc.getElementsByTagName("servers");
			NodeList contracts = doc.getElementsByTagName("contracts");
			NodeList offers = doc.getElementsByTagName("offers");
			NodeList validations = doc.getElementsByTagName("validations");
			NodeList utilities = doc.getElementsByTagName("utilities");
			NodeList intentions = doc.getElementsByTagName("intentions");
			NodeList counteroffers = doc.getElementsByTagName("counteroffers");
			NodeList extras = doc.getElementsByTagName("extras");
			
			//System.out.println("resources   = " + resources.getLength());
			//System.out.println("services    = " + services.getLength());
			//System.out.println("servers     = " + servers.getLength());
			//System.out.println("contracts   = " + contracts.getLength());
			//System.out.println("offers      = " + offers.getLength());
			//System.out.println("validations = " + validations.getLength());
			
			
			//Dekodowanie listy zasobow <resources> ... </resources> --------------------------------------------------
			if(resources.getLength()==1)
			{
				Element resourcesElement = (Element)resources.item(0);
				NodeList resourceItems = resourcesElement.getElementsByTagName("resource");
				
				for(int i=0; i<resourceItems.getLength(); i++)
				{
					Element resourceElement = (Element) resourceItems.item(i);
					resource rc = tagBuilder.getResourceInfo(resourceElement);
					this.resources.add(rc);
				}
			}
			
			//Dekodowanie listy uslug <services> ... </services> ------------------------------------------------------
			if(services.getLength()==1)
			{
				Element servicesElement = (Element) services.item(0);
				NodeList servicesList = servicesElement.getElementsByTagName("service");
				for(int i=0; i<servicesList.getLength(); i++)
				{
					Element serviceElement = (Element) servicesList.item(i);
					service srv = tagBuilder.getServiceInfo(serviceElement);
					//String type = serviceElement.getTextContent();
					this.services.add(srv);
				}
			}
			
			//Dekodowanie listy serwerow <servers> ... </servers> -----------------------------------------------------
			if(servers.getLength()==1)
			{
				Element serversElement = (Element) servers.item(0);
				NodeList serversList = serversElement.getElementsByTagName("server");
				
				for(int i=0; i<serversList.getLength(); i++)
				{
					Element serverElement = (Element) serversList.item(i);
					server srv = new server();
					srv.name = serverElement.getTextContent();
					srv.type = serverElement.getAttribute("type");
					this.servers.add(srv);
					//this.servers.add(serverElement.getTextContent());
				}
				
			}
			
			//Dekodowanie listy kontraktow <contracts> ... </contracts> -----------------------------------------------
			if(contracts.getLength()==1)
			{
				Element contractsElement = (Element) contracts.item(0);
				NodeList contractsList = contractsElement.getElementsByTagName("contract");
				
				for(int i=0; i<contractsList.getLength(); i++)
				{
					Element contractElement = (Element) contractsList.item(i);
					contract ctr = tagBuilder.getContractInfo(contractElement);
					
//					NodeList uuidList = contractElement.getElementsByTagName("uuid");
//					
//					if(uuidList.getLength()==1)
//					{
//						Element uuidElement = (Element) uuidList.item(0);
//						ctr.uuid = uuidElement.getTextContent();
//					}
					
					this.contracts.add(ctr); 
				}
			}
			
			//Dekodiwanie listy ofert <offers> ... </offers> ----------------------------------------------------------
			if(offers.getLength()==1)
			{
				Element offersElement = (Element) offers.item(0);
				NodeList offersList = offersElement.getElementsByTagName("offer");
				
				for(int i=0; i<offersList.getLength(); i++)
				{
					offer of = tagBuilder.getOfferInfo((Element) offersList.item(i));
					
					this.offers.add(of);
				}
			}
			
			//Dekodowanie listy ofert <validations> ... </validations> ------------------------------------------------
			if(validations.getLength()==1)
			{
				Element validationsElement = (Element) validations.item(0);
				NodeList validationsList = validationsElement.getElementsByTagName("validation");
				
				for(int i=0; i<validationsList.getLength(); i++)
				{
					validation va = tagBuilder.getValidationInfo((Element) validationsList.item(i));
					this.validations.add(va);
				}
			}
			
			//Dekodowanie listy ofert <utilities> ... </utilities> ----------------------------------------------------
			if(utilities.getLength()==1)
			{
				Element utilitiesElement = (Element) utilities.item(0);
				NodeList utilitiesList = utilitiesElement.getElementsByTagName("utility");
				
				for(int i = 0; i<utilitiesList.getLength(); i++)
				{					
					utility ut = tagBuilder.getUtilityInfo((Element) utilitiesList.item(i));
					this.utilities.add(ut);
				}
			}
			
			//
			if(intentions.getLength() == 1)
			{
				Element intentionsElement = (Element) intentions.item(0);
				NodeList intentionsList = intentionsElement.getElementsByTagName("intention");
				
				for(int i = 0; i<intentionsList.getLength(); i++)
				{
					intention intni = tagBuilder.getIntentionInfo((Element) intentionsList.item(i));
					this.intentions.add(intni);
				}
			}
			
			//
			if(counteroffers.getLength()==1)
			{
				Element counteroffersElement = (Element) counteroffers.item(0);
				NodeList counteroffersList = counteroffersElement.getElementsByTagName("counteroffer");
				
				for(int i = 0; i<counteroffersList.getLength(); i++)
				{
					counteroffer coffer = tagBuilder.getCounterOfferInfo((Element) counteroffersList.item(i));
					this.counteroffers.add(coffer);
				}
			}
			
			//
			if(extras.getLength()==1)
			{
				Element extrasElement = (Element) extras.item(0);
				NodeList extrasList = extrasElement.getElementsByTagName("extra");
				
				for(int i = 0; i<extrasList.getLength(); i++)
				{
					extra ext = tagBuilder.getExtraInfo((Element) extrasList.item(i));
					this.extras.add(ext);
				}
			}
			
			//System.out.println(resources.getLength() + " " + services.getLength() + " " + servers.getLength() + " " + contracts.getLength() + " " + offers.getLength());
			//Element resourcesElement = (Element)resources.item(0);
			
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			logger.error("ParserConfigurationException", e);
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			logger.error("SAXException", e);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			logger.error("IOException", e);
		}
		
	}

}
